package faw.car.ccentersdk.prop;

import android.os.Parcel;
import android.os.Parcelable;

import org.json.JSONObject;

import java.lang.reflect.Array;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author zhuima
 */
public class HQCarPropertyExtParameter implements Parcelable {

    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
    public static final String KEY_AREA = "area";
    public static final String KEY_IDENTIFY = "identify";

    private static final int TYPE_PARCELABLE_ARR = 1;
    private static final int TYPE_OTHER = 0;

    private Map<String, Object> mExtParams = new HashMap<>();

    public static HQCarPropertyExtParameter builder() {
        return new HQCarPropertyExtParameter();
    }

    public HQCarPropertyExtParameter area(int area) {
        mExtParams.put(KEY_AREA, area);
        return this;
    }

    public int getArea() {
        if (mExtParams.containsKey(KEY_AREA) && mExtParams.get(KEY_AREA) != null) {
            return (int) mExtParams.get(KEY_AREA);
        }
        return 1;
    }

    public int getIdentify() {
        if (mExtParams.containsKey(KEY_IDENTIFY) && mExtParams.get(KEY_IDENTIFY) != null) {
            return (int) mExtParams.get(KEY_IDENTIFY);
        }
        return 0;
    }

    public HQCarPropertyExtParameter identify(int identify) {
        mExtParams.put(KEY_IDENTIFY, identify);
        return this;
    }

    public HQCarPropertyExtParameter addParameter(String key, Object value) {
        mExtParams.put(key, value);
        return this;
    }

    public HQCarPropertyExtParameter addParameters(Map<String, Object> values) {
        if (values != null) {
            mExtParams.putAll(values);
        }
        return this;
    }

    public boolean hasParameters() {
        if (mExtParams == null || mExtParams.isEmpty()) {
            return false;
        }
        switch (mExtParams.size()) {
            case 1:
            case 2:
                return false;
            case 3:
                return !mExtParams.containsKey(KEY_IDENTIFY);
        }
        return true;
    }

    public Object get(String key) {
        return mExtParams.get(key);
    }

    public <T> T get(String key, Class<T> clazz) {
        final Object target = mExtParams.get(key);
        if (target != null && target.getClass() == clazz) {
            return (T) target;
        }
        return null;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        if (mExtParams == null || mExtParams.isEmpty()) {
            dest.writeInt(0);
        } else {
            dest.writeInt(this.mExtParams.size());
            for (Map.Entry<String, Object> entry : this.mExtParams.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                dest.writeString(key);
                if (value instanceof Parcelable[]) {
                    final Parcelable[] arr = (Parcelable[]) value;
                    final Parcelable first = arr[0];
                    dest.writeInt(TYPE_PARCELABLE_ARR);
                    dest.writeString(first.getClass().getName());
                    dest.writeInt(arr.length);
                    for (Parcelable parcelable : ((Parcelable[]) value)) {
                        dest.writeParcelable(parcelable, flags);
                    }
                } else {
                    dest.writeInt(TYPE_OTHER);
                    Class<?> valueClass = value == null ? null : value.getClass();
                    dest.writeString(valueClass == null ? null : valueClass.getName());
                    if (String.class.equals(valueClass)) {
                        final byte[] bytes = ((String) value).getBytes(DEFAULT_CHARSET);
                        dest.writeInt(bytes.length);
                        dest.writeByteArray(bytes);
                    } else if (byte[].class.equals(valueClass)) {
                        dest.writeInt(((byte[]) value).length);
                        dest.writeByteArray((byte[]) value);
                    } else {
                        dest.writeValue(value);
                    }
                }
            }
        }
    }

    public void readFromParcel(Parcel source) {
        parseParcel(source);
    }

    private void parseParcel(Parcel source) {
        if (source == null) {
            return;
        }
        int mExtParamsSize = source.readInt();
        if (mExtParamsSize > 0) {
            this.mExtParams = new HashMap<String, Object>(mExtParamsSize);
            for (int i = 0; i < mExtParamsSize; i++) {
                String key = source.readString();
                Object value = null;
                int parcelableArrayFlag = source.readInt();
                String valueClassName = source.readString();
                if (valueClassName == null) {
                    value = null;
                } else {
                    Class<?> valueClass;
                    try {
                        valueClass = Class.forName(valueClassName);
                        if (parcelableArrayFlag == TYPE_PARCELABLE_ARR) {
                            int size = source.readInt();
                            Object[] valueArr = (Object[]) Array.newInstance(valueClass, size);
                            for (int i1 = 0; i1 < size; i1++) {
                                valueArr[i1] = source.readParcelable(valueClass.getClassLoader());
                            }
                            value = valueArr;
                        } else {
                            if (String.class.equals(valueClass)) {
                                int len = source.readInt();
                                byte[] bytes = new byte[len];
                                source.readByteArray(bytes);
                                value = new String(bytes, DEFAULT_CHARSET);
                            } else if (byte[].class.equals(valueClass)) {
                                int len = source.readInt();
                                byte[] bytes = new byte[len];
                                source.readByteArray(bytes);
                                value = bytes;
                            } else {
                                value = source.readValue(valueClass.getClassLoader());
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        value = null;
                    }
                }
                mExtParams.put(key, value);
            }
        }
    }

    public HQCarPropertyExtParameter() {
    }

    protected HQCarPropertyExtParameter(Parcel in) {
        parseParcel(in);
    }

    public static final Creator<HQCarPropertyExtParameter> CREATOR = new Creator<HQCarPropertyExtParameter>() {
        @Override
        public HQCarPropertyExtParameter createFromParcel(Parcel source) {
            return new HQCarPropertyExtParameter(source);
        }

        @Override
        public HQCarPropertyExtParameter[] newArray(int size) {
            return new HQCarPropertyExtParameter[size];
        }
    };

    @Override
    public String toString() {
        if (mExtParams != null) {
            JSONObject jsonObject = new JSONObject(mExtParams);
            return "HQCarPropertyExtParameter{" +
                    "mExtParams=" + jsonObject +
                    '}';
        } else {
            return "HQCarPropertyExtParameter {mExtParams=null}";
        }
    }
}
